/**
 * @file analyser.h
 * @author Florian Castellane <florian.castellane@grenoble-inp.org>
 * @author Christian Proust <christian.proust@phelma.grenoble-inp.fr>
 * @date Wed Sep 26 14:34:00 2012
 * @brief Fonctions d'analyse de texte.
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "include/gestiondetypes.h"
#include "include/analyser.h"
#include "include/auto_nombre.h"


LISTE decoupe_ligne_lexeme(char* ligne_arg, LISTE liste_de_lexemes)
{
    char* lexeme_en_lecture;
    char separateur[] = " " ;
    ELEMENT lexeme_decoupe;
    int nb_lexemes_isoles=0;
    // tant qu'on peut lire des lexemes separes par des espaces
    for (lexeme_en_lecture = strtok(ligne_arg, separateur); lexeme_en_lecture ; lexeme_en_lecture = strtok(NULL, separateur))
    {
        lexeme_decoupe.chaine_lexeme=strdup(lexeme_en_lecture); // on les prend un par un
        liste_de_lexemes=ajout_queue(lexeme_decoupe, liste_de_lexemes);       // et on les enregistre
        nb_lexemes_isoles++;
    }

    //printf("%d lexemes isoles\n", nb_lexemes_isoles);

    // on peut logger ligne_arg pour voir si éventuellement il reste des choses non lues
    return liste_de_lexemes;
}

LISTE classifie_liste_lexeme(LISTE liste_de_lexemes)
{
    LISTE liste_index=liste_de_lexemes;
    int nb_lexemes_classifies=0;

    while (!est_vide(liste_index))
        // pour chaque lexeme de la liste
    {
        liste_index->element.type_de_lexeme = classifie_lexeme(liste_index->element.chaine_lexeme);

		if(liste_index->element.type_de_lexeme == TYPE_Commentaire)
		{
			while(strcmp(liste_index->suivant->element.chaine_lexeme,"\n") !=0)
			//on s'astreindra a toujours avoir un \n en fin de fichier
			{   // attention si le fichier contient un seul lexeme ou si le commentaire est en un seul lexeme
				liste_index = liste_index->suivant;
				liste_index->element.type_de_lexeme=TYPE_Commentaire;
				nb_lexemes_classifies++;
			}
		}
        liste_index = liste_index->suivant;
        nb_lexemes_classifies++;
        // log le type de lexeme lu et le lexeme lui meme
    }

    printf("%d lexeme(s) a (ont) ete classifie(s).\n\n", nb_lexemes_classifies); // peut aussi etre log

    return liste_de_lexemes;
}

TYPE_DE_LEXEME classifie_premier_caractere(char caractere_en_cours)
{
    switch(caractere_en_cours)
    {
    case '\n':
        return TYPE_Nouvelle_ligne;
        break;

    case '#':
        return TYPE_Commentaire;    // il faudra traiter tous les lexemes suivants comme du commentaire
        break;

    case '.':
        return TYPE_Directive;
        break;

    case ',':
        return TYPE_Virgule;
        break;

    case '$':
        return TYPE_Operande;
        break;

    case '-':
        return TYPE_Minus;
        break;

    case '+':
        return TYPE_Plus;
        break;

    case '(':
        return TYPE_Parenthese_gauche;
        break;

    case ')':
        return TYPE_Parenthese_droite;
        break;

    case ':':
        // logger
        printf("\"%c\" est deux points",caractere_en_cours);
        return TYPE_Erreur; // car un deux points doit suivre un nom d'etiquette, il ne peut pas etre tout seul
        break;

    default:
        // loguer le caractère pour voir si on n'a pas oublié des types
        return -1;
        break;

    }   // end switch
    return -1;
}

TYPE_DE_LEXEME classifie_lexeme(char* lexeme)
{
    int i=0;
    int STATUT=STATUT_INIT;
    int caractere_en_cours;
    TYPE_DE_LEXEME type_en_cours=TYPE_Erreur;
    TYPE_DE_LEXEME type_premiercaractere;
    TYPE_DE_LEXEME type_nombre;

    for(i=0; i<strlen(lexeme); i++)
    {
        caractere_en_cours = lexeme[i];
        switch (STATUT)
        {
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        case STATUT_INIT:

            type_premiercaractere = classifie_premier_caractere(caractere_en_cours);
            if ((int)type_premiercaractere>=0) 
            {
            	if(type_premiercaractere==TYPE_Commentaire) return type_premiercaractere;
            	if(type_premiercaractere==TYPE_Nouvelle_ligne ||type_premiercaractere==TYPE_Minus ||  type_premiercaractere==TYPE_Parenthese_droite || type_premiercaractere==TYPE_Parenthese_gauche || type_premiercaractere==TYPE_Plus ||type_premiercaractere==TYPE_Virgule)  
            	{	//si le lexeme doit etre isole
            		if(strlen(lexeme)==1) return type_premiercaractere;
            		printf("Erreur : le caractere '%c'n'est pas isole",caractere_en_cours);
            		return TYPE_Erreur;
            	}
            	
            	type_en_cours=type_premiercaractere;
            	STATUT=STATUT_VERIF;
            	break;
			}
            if ( !isalnum(caractere_en_cours) )
            {
                // Si le caractère n'est pas une lettre (chiffre, symbole, choses pas
                // trop normales comme des caractères pas affichables en ASCII, etc)
                printf("\'%c\' n\'est pas un caractere alphanumerique\n",caractere_en_cours);
                return TYPE_Erreur;
                // logger le caractère
            }

            if ( isspace(caractere_en_cours))
                /*
                checks for white-space characters.
                In the "C" and "POSIX" locales, these are:
                space,
                form-feed ('\f'),
                newline ('\n'),
                carriage return ('\r'),
                horizontal tab ('\t'),
                and vertical tab ('\v').
                */
            {
                // on ne doit pas avoir d'espace / tabulation / saut de ligne (traité plus haut) /  à ce niveau
                // ils ont étés éliminés par la canonisation notamment
                // logger l'erreur
                printf("\'%c\' est un caractere d\'espacement invalide\n",caractere_en_cours);
                return TYPE_Erreur;
            }

            if ( isalpha(caractere_en_cours) || caractere_en_cours == '_')  // alphabetique
            {
                // ça peut être une instruction, une étiquette, une opérande...
                type_en_cours = TYPE_Symbole;
                STATUT = STATUT_SYMBOLE;
            }

            if ( isdigit(caractere_en_cours) )  // si le lexeme commence par un chiffre
            {
                // pour l'identifier, on utilise la fonction classifie_nombre
                // or son argument, une chaine, doit se terminer par un espace
                // on ajoute donc un espace en fin de chaine avant de l'appeler

                //printf("Caught digit \"%c\"\n",lexeme[i]);

                char* ligne = NULL;
                ligne = malloc((strlen(lexeme)+1)*sizeof(char));
                if (ligne==NULL)
                {
                    perror("Erreur d\'allocation memoire dans classifie_nombre");
                    exit(EXIT_FAILURE);
                }
                sprintf(ligne,"%s ",lexeme);
                type_nombre = classifie_nombre(ligne);
                free(ligne);
                return type_nombre;
            }

            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        case STATUT_SYMBOLE:
            if (caractere_en_cours==':'&& i==strlen(lexeme)-1)    // si le symbole contient ':'
            {
                return TYPE_Etiquette; // ceci peut se trouver dans un commentaire
            }
            
            if(!(isalpha(caractere_en_cours) || caractere_en_cours == '_' || isdigit(caractere_en_cours) ) )
            	{
            		printf("Erreur : le caractere '%c' est invalide",caractere_en_cours);
            		return TYPE_Erreur; 
            	} 
            STATUT=STATUT_SYMBOLE;
            break;
            //////////////////////////////////////////////////////////////////////////////////////
        case STATUT_VERIF:
        
        	if(type_en_cours==TYPE_Directive && (!(isalpha(caractere_en_cours)) ) )
			{
				printf("Erreur : directive incorecte");
				return TYPE_Erreur;
			}
			
			if(type_en_cours==TYPE_Operande && ( !(isalpha(caractere_en_cours) || isdigit(caractere_en_cours)) )  )
			{
				printf("Erreur : operande incorrecte");
				return TYPE_Erreur;
			}
			
			break;

        }   // end switch STATUT

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    }   // end for
    return type_en_cours;
}


void canoniser_ligne(char* texte)
{
    int i=0;

    //printf("\n\n\tPartieI:\n%s",texte);

    /// PARTIE I : On commence par remplacer les tabulations par des espaces
    while(texte[i] != '\0')
    {
        if (texte[i] == '\t'||texte[i] == '\r') texte[i] = ' ';      
        i++;
    }

    //printf("\n\n\tPartieII:\n%s",texte);

    /// PARTIE II : On rajoute ensuite les espaces qui manquent
    i=0;
    while(texte[i] != '\0')
    {
        if (texte[i] == ',' || texte[i] == ')' ||texte[i] == '(' || texte[i] == '+' || texte[i] == '-'|| texte[i] == ':' || texte[i]=='\n')
        {

            ajouter_caractere_tableau(texte,i+1, ' ');
            i++;
            ajouter_caractere_tableau(texte,i-1, ' ');
        }


        i++;
    }


    //printf("\n\n\tPartieIII:\n%s",texte);
    /// PARTIE III On supprime enfin les espaces en trop
    i=0;
    while(texte[i] != '\0')
    {
        while((texte[i] == ' ')&&(texte[i+1]==' '|| texte[i+1] == ':'))
        {
            //printf("\ncaractere supprime : '%c'", texte[i]);
            supprimer_caractere_tableau(texte,i);

        }

        i++;
    }


}


void supprimer_caractere_tableau(char * tableau, int offset)
{
    int i=offset;
    while(tableau[i] != '\0')
    {
        tableau[i]=tableau[i+1];
        i++;
    }
}

void ajouter_caractere_tableau(char* tableau,int offset,char caractere)
{
    if (tableau[offset]=='\0') return;
    decaler_caractere_vers_droite(tableau, offset);
    tableau[offset]=caractere;
}
//FILE *fichier= ouvrir_fichier(fichier_source,TAILLE_MAX_LIGNE)

void decaler_caractere_vers_droite(char* tableau,int offset)
{
    if (tableau[offset] != '\0') decaler_caractere_vers_droite(tableau,offset+1);

    tableau[offset+1] = tableau[offset];
}

